package cn.bitkit.tools.file;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.List;

import cn.bitkit.base.util.StringUtil;
import cn.bitkit.tools.common.IOUtil;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 对文件的一次性操作
 * </br>连续操作可以使用Files.newXXX();
 * @author changbo
 * 
 */
@Slf4j
public class FileUtil {

	public static byte[] readAsBytes(File file) {
		return readAsBytes(file.getAbsolutePath());
	}

	@SneakyThrows
	public static byte[] readAsBytes(String fileName) {
		return Files.readAllBytes(Paths.get(fileName));
	}

	@SneakyThrows
	public static String readAsString(String fileName) {
		return Files.readString(Paths.get(fileName), StandardCharsets.UTF_8);
	}

	@SneakyThrows
	public static String readAsString(String fileName, Charset cs) {
		return Files.readString(Paths.get(fileName), cs);
	}

	@SneakyThrows
	public static String readAsBase64(String fileName) {
		byte[] bytes = readAsBytes(fileName);
		return StringUtil.encodeBase64(bytes);
	}
	
	
	public static boolean delete(String fileName) {
		return delete(new File(fileName));
	}

	public static boolean delete(File file) {
        if (!file.exists()) {
            return false;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
            	delete(f);
            }
        }
        return file.delete();
	}
	
	public static void appendLine(String fileName) {
		appendLine(fileName, new byte[] {});
	}

	@SneakyThrows
	public static void appendLine(String fileName, byte[] bytes) {
		Path path = Paths.get(fileName);
		Files.createDirectories(path.getParent());
		Files.createFile(path);
		Files.write(path, bytes, StandardOpenOption.APPEND);
	}

	@SneakyThrows
	public static void appendLine(String fileName, String str) {
		createFile(fileName);
		BufferedWriter writer = Files.newBufferedWriter(Paths.get(fileName), StandardOpenOption.APPEND);
		writer.write(str+"\n");
		writer.flush();
		writer.close();
	}

	public static List<String> readAllLines(String fileName) {
		return readAllLines(fileName, StandardCharsets.UTF_8);
	}

	@SneakyThrows
	public static List<String> readAllLines(String fileName, Charset cs) {
		return Files.readAllLines(Paths.get(fileName), cs);
	}

	@SneakyThrows
	public static void write(String fileName, String text){
		write(fileName, text.getBytes());
	}

	@SneakyThrows
	public static void write(String fileName, byte[] bytes){
		IOUtil.write(new ByteArrayInputStream(bytes), new FileOutputStream(fileName));
	}

	@SneakyThrows
	public static void writeOutputStream(OutputStream out, File file){
		IOUtil.write(new FileInputStream(file), out);
	}

	@SneakyThrows
	public static void writeFile(File file, InputStream in){
		IOUtil.write(in, new FileOutputStream(file));
	}

	@SneakyThrows
	public static void writeFile(String file, InputStream in){
		IOUtil.write(in, new FileOutputStream(file));
	}

	@SneakyThrows
	public static void copyFile(String fromFile, String toFile){
		Path fromPath = Paths.get(fromFile);
		Path toPath = Paths.get(toFile);
		Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING);
	}
	
	public static void copyFolder(String fromFile, String toFile){
		File fromFolder = new File(fromFile);
		File toFolder = new File(toFile);
		if(fromFolder.isFile()) {
			return;
		}
		createFolder(toFolder.getAbsolutePath());
		String[] files = fromFolder.list();
		if(files == null) {
			return;
		}
		String from = fromFolder.getAbsolutePath().replaceAll("\\\\", "/");
		String to = toFolder.getAbsolutePath().replaceAll("\\\\", "/");;
        for (String file : files) {
            if ((new File(from + "/" + file)).isDirectory()) {
                copyFolder(from + "/" + file, to + "/" + file);
            }
            if (new File(from + "/" + file).isFile()) {
                copyFile(from + "/" + file, to + "/" + file);
            }
        }
	}

	@SneakyThrows
	public static void createFileParent(String fileName){
		Path path = Paths.get(fileName);
		Path parent = path.getParent();
		if(parent == null) {
			return;
		}
		if(!parent.toFile().exists()) {
			createFolder(parent.toString());
		}

	}

	@SneakyThrows
	public static void createFile(String fileName){
		Path path = Paths.get(fileName);
		Path parent = path.getParent();
		if(parent == null) {
			return;
		}
		if(!parent.toFile().exists()) {
			createFolder(parent.toString());
		}
		if(!path.toFile().exists()) {
			Files.createFile(path);
		}
	}

	@SneakyThrows
	public static void createFolder(String folderName){
		Path path = Paths.get(folderName);
		if(!path.toFile().exists()) {
			Files.createDirectories(path);
		}
	}

	
	public static void createFile(String fileName, String text){
		createFile(fileName, text.getBytes());
	}

	@SneakyThrows
	public static void createFile(String fileName, byte[] bytes){
		createFile(fileName);
		OutputStream os = Files.newOutputStream(Paths.get(fileName), StandardOpenOption.TRUNCATE_EXISTING);
		os.write(bytes);
		os.flush();
		os.close();
	}
	
	public static void rename(String fileName, String newFileName){
		File file = new File(fileName);
		createFile(fileName);
    	file.renameTo(new File(newFileName));
	}
	
	public static String getSuffix(File file){
	    return getSuffix(file.getName());
	}
	
	public static String getSuffix(String fileName){
		if(StringUtil.isEmpty(fileName)){
			return fileName;
		}
		int index = fileName.lastIndexOf(".");
		if (index > 0 && fileName.length() - index <= 20) {
			return fileName.substring(index + 1);
		}
		return "";
	}

	public static String addSuffix(String fileName, String suffix){
		suffix = StringUtil.isEmpty(suffix) ? "" : "." + suffix;
		return fileName + suffix;
	}

	@SneakyThrows
	public static String readTail(String fileName, int length) {
		ByteBuffer buffer = ByteBuffer.allocate(length);
		@Cleanup
		FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
		channel.read(buffer, channel.size() - length);
		return new String(buffer.array());
	}

	public static void splitFile(String fileName, int size) {
		splitFile(fileName, size, "UTF-8");
	}

	@SneakyThrows
	public static void splitFile(String fileName, int size, String charset) {
		BufferedReader br = Files.newBufferedReader(Paths.get(fileName), Charset.forName(charset));
		BufferedWriter bw = null;
		int index = 0;
		String line = null;
		while((line = br.readLine()) != null) {
			int num = (index/size)+1;
			if(index%size == 0) {
				if(bw != null) {
					bw.flush();
					bw.close();
				}
				String newFileName = fileName+"."+ StringUtil.leftPad(""+num, '0', 3);
				createFile(newFileName);
				bw = Files.newBufferedWriter(Paths.get(newFileName), Charset.forName(charset), StandardOpenOption.TRUNCATE_EXISTING);
			}
			bw.write(line);
			bw.write("\n");
			index++;
		}
		if((index-1)%size != 0) {
			if(bw != null) {
				bw.flush();
				bw.close();
			}
		}
		br.close();
	}
	
	public static void mergeFile(String fileName, List<String> sourceList) {
		mergeFile(fileName, sourceList, "UTF-8");
	}

	@SneakyThrows
	public static void mergeFile(String fileName, List<String> sourceList, String charset) {
		createFile(fileName);
		BufferedWriter bw = Files.newBufferedWriter(Paths.get(fileName), Charset.forName(charset), StandardOpenOption.TRUNCATE_EXISTING);
		for(String source : sourceList) {
			BufferedReader br = Files.newBufferedReader(Paths.get(source), Charset.forName(charset));
			String line = null;
			while((line = br.readLine()) != null) {
				bw.write(line);
				bw.write("\n");
			}
			br.close();
		}
        bw.flush();
        bw.close();
    }
	public static String getMediaType(File file){
		return getMediaType(file.getAbsoluteFile());
	}

	public static String getMediaType(String filePath){
		try {
			return Files.probeContentType(Path.of(filePath));
		} catch (IOException e) {
			return "application/octet-stream";
		}
	}


	public static String getTextCharset(String fileName) {
		try {
			@Cleanup
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
			bis.mark(0);
			byte[] head = new byte[3];
			int read = bis.read(head);
			if(read == -1){
				//文件编码为 ANS
				return "GBK";
			}
			if (head[0] == (byte)0xFF && head[1] == (byte)0xFE){
				//文件编码为 Unicode
				return "UTF-16LE";
			}
			if (head[0] == (byte)0xFE && head[1] == (byte)0xFF){
				//文件编码为 Unicode big endiar
				return "UTF-16BE";
			}
			if (head[0] == (byte) 0xEF && head[1] == (byte)0xBB && head[2]==(byte)0xBF) {
				//文件编码为 UTF-8
				return  "UTF-8";
			}
			long fileSize = new File(fileName).length();
			//最大只读10kb的数据
			long maxRead = Math.min(fileSize, 10240);
			int index = 0;
			bis.reset();
			while ((read = bis.read()) != -1 && index<maxRead) {
				index++;
				if (read >= 0xF0){
					return "GBK";
				}
				// 单独出现BF以下的，也算是GBK
				if (0x80 <= read && read <= 0xBF){
					return "GBK";
				}
				if (0xc0 <= read && read <= 0xDF) {
					read = bis.read();
					// 双字节(0xC0 - 0XDF) (0x80 - 0xBF)，也可能在GBK编码内
					if (0x80 <= read && read <= 0xBF){
						continue;
					}else{
						return "GBK";
					}
				} else if(0xE0 <= read && read <= 0xEF) {
					// 也有可能出错，但是几率较小
					read = bis.read();
					if (0x80 <= read && read <= 0xBF) {
						read = bis.read();
						if (0x80 <= read && read <= 0xBF) {
							return "UTF-8";
						}
					}
					break;
				}
			}
		} catch(Exception e){
			log.error("", e);
		}
		return "GBK";
	}


}
